
#include "t0022-0.h"
#include "cui.h"
#include "dxtviewer.h"
#include <dlfcn.h>

#define MODNAME     "[CUI]"

const char *cui_key_str(int key)
{
	char *keystr = "<UNK>";
	switch (key) {
		case KEY_DOWN:
			keystr = "<DOWN>";
			break;
		case KEY_UP:
			keystr = "<UP>";
			break;
		case KEY_NPAGE:
			keystr = "<NPAGE>";
			break;
		case KEY_PPAGE:
			keystr = "<PPAGE>";
			break;
        case KEY_RIGHT:
            keystr = "<RIGHT>";
            break;
		case KEY_ESC:
			keystr = "<ESC>";
			break;
        case KEY_ENTER:
            keystr = "<ENTER>";
            break;
        case KEY_ENTER_US:
            keystr = "<ENTER>";
            break;
        case KEY_BACKSPACE_US:
            keystr = "<BACKSPACE>";
            break;
        case KEY_SPACE:
            keystr = "<KEY_SPACE>";
            break;
        case KEY_TAB:
            keystr = "<KEY_TAB>";
            break;
	}
	return keystr;
}

void cui_trim_buf(char *buf)
{
	int len = strlen(buf);
	int i;

	for (i = len - 1;i >= 0;i --) {
		if (buf[i] == ' ')
			buf[i] = 0;
		else
			break;
	}
}

void cui_print(WINDOW *win, int starty, int startx, int width, char *string, int color, int alignment)
{	int length, x, y;
	int temp;
    char *str = calloc(1, width + 1);

	if(win == NULL)
		win = stdscr;
	getyx(win, y, x);
	if(startx != -1)
		x = startx;
	if(starty != -1)
		y = starty;
	if(width == 0)
		width = 80;
    
    length = strlen(string);
    memset(str, ' ', width);
    str[width] = 0;

    ERRSYS_INFOPRINT("width %d length %d string %s\n", width, length, string);

    if (width < length) {
        length = width;
    }
    memcpy(str, string, length);

    if (alignment == CUI_PRINT_ALIGNMENT_MIDDLE) {
        temp = (width - length)/ 2;
        x += temp;
    }

	wattron(win, COLOR_PAIR(color));
	mvwprintw(win, y, x, string);
    ERRSYS_INFOPRINT("cui print y:%d x:%d \"%s\"\n", y, x, str);
	wattroff(win, COLOR_PAIR(color));
	refresh();
}

/*
* 在widget窗口内打印文字，并且自动换行。starty,startx为widget窗口的边框内的的坐标，不包括边框边框。也就是说
* cui_print_autoreturn无法将文字打印到边框上，只能在窗口内打印
*/
void cui_print_autoreturn(struct cui_widget_st *widget, int starty, int startx, const char *string, int color)
{
    int rows = widget->lines - CUI_WIDGET_BORDER_WIDTHX2;
    int cols = widget->cols - CUI_WIDGET_BORDER_WIDTHX2;
    WINDOW *win;
    int i, j;
    ERRSYS_INFOPRINT("%s rows %d cols %d\n", __func__, rows, cols);
    if (rows > 0 && cols > 0) {
        char *buf = zmalloc(cols + 1);
        int offset = 0;
        win = widget->win;
        wattron(win, COLOR_PAIR(color));
        j = CUI_WIDGET_BORDER_WIDTH;
        for (i = 0;i < rows;i ++) {
            mvwprintw(win, j, CUI_WIDGET_BORDER_WIDTH, "%*s", cols, "");
            j ++;
        }
        if (string && strlen(string)) {
            for (i = 0;i < rows;i ++) {
                if (i != 0) {
                    startx = 0;
                }

                snprintf(buf, cols + 1 - startx, "%s", string + offset);
                mvwprintw(win, starty + CUI_WIDGET_BORDER_WIDTH, startx + CUI_WIDGET_BORDER_WIDTH, buf);
                offset += strlen(buf);
                starty ++;
            }
        }

        wattroff(win, COLOR_PAIR(color));
    }
}

int cui_autoreturn_rows(struct cui_widget_st *widget, int startx, const char *string)
{
    int rows = widget->lines - CUI_WIDGET_BORDER_WIDTHX2;
    int cols = widget->cols - CUI_WIDGET_BORDER_WIDTHX2;
    int i;
    int starty = 0;
    ERRSYS_INFOPRINT("%s rows %d cols %d\n", __func__, rows, cols);
    if (rows > 0 && cols > 0) {
        char *buf = zmalloc(cols + 1);
        int offset = 0;

        if (string && strlen(string)) {
            for (i = 0;i < rows;i ++) {
                if (i != 0) {
                    startx = 0;
                }

                snprintf(buf, cols + 1 - startx, "%s", string + offset);
                offset += strlen(buf);
                starty ++;
                if (offset >= strlen(string)) {
                    break;
                }
            }
        }
    }
    return starty;
}

void cui_unload(struct cui_screen_st *cscr, int setendwin)
{
    cui_ops_widget_recursive_depthfirst(&cscr->windowlist, CUI_WIDGET_OPS_RECURSIVE_TYPE_DESTROY);
    free(cscr);
    if (setendwin) {
        endwin();
    }
    ERRSYS_INFOPRINT("cui unloaded\n");
}

static void cui_initialize_palettes(void)
{
	/* Initialize all the colors */
	init_pair(REDBLK, COLOR_RED, COLOR_BLACK);
	init_pair(GRNBLK, COLOR_GREEN, COLOR_BLACK);
	init_pair(BLUBLK, COLOR_BLUE, COLOR_BLACK);
	init_pair(CYNBLK, COLOR_CYAN, COLOR_BLACK);
    init_pair(YELBLK, COLOR_YELLOW, COLOR_BLACK);
    init_pair(WHTBLK, COLOR_WHITE, COLOR_BLACK);

	init_pair(BLKRED, COLOR_BLACK, COLOR_RED);
	init_pair(BLKGRN, COLOR_BLACK, COLOR_GREEN);
	init_pair(BLKBLU, COLOR_BLACK, COLOR_BLUE);
	init_pair(BLKCYN, COLOR_BLACK, COLOR_CYAN);
    init_pair(BLKYEL, COLOR_BLACK, COLOR_YELLOW);
	init_pair(BLKWHT, COLOR_BLACK, COLOR_WHITE);
}

void cui_initialize_screen(void)
{
    freopen("/dev/null", "w", stderr);
	// freopen("/dev/null", "w", stdout);   关闭stdout，libncurses无法正常显示
	initscr();
	start_color();
	cbreak();
	noecho();
	curs_set(CUI_CURSOR_INVISIBLE);
	// keypad(stdscr, TRUE);
    // ERRSYS_INFOPRINT("[stdscr] binding keypad\n");

    ERRSYS_INFOPRINT("initialize screen\n");
    return;
}

//lines/cols=-1，重新瓜分剩余的高度/宽度
int cui_attributes_parse(cJSON *root, struct cui_widget_st* widget, struct cui_widget_operations *widgetops, void *widgetinstance)
{
    int retval = -1;
    cJSON *node, *attr, *nodesize;

    memcpy(&widget->ops, widgetops, sizeof(struct cui_widget_operations));
    widget->widgetinstance = widgetinstance;

    // attributes
	attr = cJSON_GetObjectItem(root, CUI_CONFIELD_ATTRIBUTES);
	if(attr == NULL) {
		ERRSYS_FATALPRINT("missing attributes for windows\n");
        goto err;
	}

    // cui_dump_widget(widget);
    node = cJSON_GetObjectItem(attr, CUI_CONFIELD_NAME);
    if (cJSON_ValidString(node)) {
        // int n = strlen(node->valuestring) + 1;  // with terminating null byte
        // printf("n = %d %s\n", n, node->valuestring);
        strncpy(widget->name, node->valuestring, CUI_WIDGETTITLE_BUFLEN);
    }

    ERRSYS_INFOPRINT("[%s] default cols %d lines %d\n", widget->name, COLS, LINES);

    node = cJSON_GetObjectItem(attr, CUI_CONFIELD_LAYOUT);
    if (cJSON_ValidString(node)) {
        cui_set_widget_layout(widget, node->valuestring);
    }
    else {
        nodesize = cJSON_GetObjectItem(attr, CUI_CONFIELD_SIZE);
        if (nodesize == NULL) {
            ERRSYS_INFOPRINT("missing size for windows set as full screen\n");
        }
        else {
            // 先计算beginx，beginy
            node = cJSON_GetObjectItem(nodesize, CUI_CONFIELD_BEGINX);
            if (cJSON_ValidNumber(node)) {
                widget->beginx = node->valueint;
            }
            node = cJSON_GetObjectItem(nodesize, CUI_CONFIELD_BEGINY);
            if (cJSON_ValidNumber(node)) {
                widget->beginy = node->valueint;
            }

            node = cJSON_GetObjectItem(nodesize, CUI_CONFIELD_LINES);
            if (cJSON_ValidNumber(node)) {
                if (node->valueint != 0) {
                    widget->lines = node->valueint;
                }
                else if (node->valuedouble != 0) {
                    double linespercent = node->valuedouble;
                    if (linespercent < 1 && linespercent > 0) {
                        widget->lines = widget->parent->lines * linespercent;
                        ERRSYS_INFOPRINT("[%s]%s linespercent %f set to %d\n", widget->name, __func__, linespercent, widget->lines);
                    }
                    else {
                        ERRSYS_INFOPRINT("[%s]%s invalid linespercent %f\n", widget->name, __func__, linespercent);
                    }
                }
                else if (node->valueint == 0) {
                    widget->lines -= widget->beginy;
                }
            }

            node = cJSON_GetObjectItem(nodesize, CUI_CONFIELD_COLS);
            if (cJSON_ValidNumber(node)) {
                if (node->valueint != 0) {
                    widget->cols = node->valueint;
                }
                else if (node->valuedouble != 0) {
                    double colpercent = node->valuedouble;
                    if (colpercent < 1 && colpercent > 0) {
                        widget->cols = widget->parent->cols * colpercent;
                        ERRSYS_INFOPRINT("[%s]%s colpercent %f set to %d\n", widget->name, __func__, colpercent, widget->cols);
                    }
                    else {
                        ERRSYS_INFOPRINT("[%s]%s invalid colpercent %f\n", widget->name, __func__, colpercent);
                    }
                }
                else if (node->valueint == 0) {
                    widget->cols -= widget->beginx;
                }
            }
        }
    }
    
    widget->flags = CUI_WIDGET_FLAGS_NONE;
    node = cJSON_GetObjectItem(attr, CUI_CONFIELD_BORDER);
    if (cJSON_ValidNumber(node)) {
        if (node->valueint == 1) {
            widget->flags |= CUI_WIDGET_FLAGS_BORDER;
        }
    }
    
    node = cJSON_GetObjectItem(attr, CUI_CONFIELD_TITLE);
    if (cJSON_ValidString(node)) {
        strncpy(widget->title, node->valuestring, CUI_WIDGETTITLE_BUFLEN);
        ERRSYS_INFOPRINT("[%s]%s enable title\n", widget->name, __func__);
        widget->flags |= CUI_WIDGET_FLAGS_TITLE;
    }
    ERRSYS_INFOPRINT("[%s] cols %d lines %d\n", widget->name, widget->cols, widget->lines);
    ERRSYS_INFOPRINT("[%s] title: \"%s\" flags %d\n", widget->name, widget->title, widget->flags);

    cui_label_parse(attr, widget);
    cui_eventslots_parse(attr, widget);
    cui_install_initializer(widget);

    retval = 0;
err:
    return retval;
}

static int cui_initialize(struct cui_screen_st *cscr, const char *json)
{
	int i;
	cJSON *root = NULL;
	cJSON *node, *windowlist, *window;
    int nwindows = 0;
    int retval = -1;

    cui_initialize_operation();
    cui_initialize_screen();
    cui_initialize_palettes();


	root = cJSON_Parse(json);
	if(root == NULL){
		ERRSYS_FATALPRINT("wrong format of json in parsing cuiconf\n");
		goto err;
	}

    node = cJSON_GetObjectItem(root, CUI_CONFIELD_VERSION);
    if (cJSON_ValidNumber(node) == 0) {
        ERRSYS_FATALPRINT("cui config version invalid\n");
        goto err;
    }
    if (node->valueint != CUI_CONFIELD_VERSION_VALUE) {
        ERRSYS_FATALPRINT("cui config version %d not supported\n", node->valueint);
        goto err;
    }

    windowlist = cJSON_GetObjectItem(root, CUI_CONFIELD_WINDOWLIST);
	if(windowlist == NULL){
		ERRSYS_FATALPRINT("cui config missing %s\n", CUI_CONFIELD_WINDOWLIST);
		goto err;
	}
    if (cJSON_ValidArray(windowlist)) {
        struct cui_widget_st *widgetprev = NULL;
        nwindows = cJSON_GetArraySize(windowlist);

        ERRSYS_INFOPRINT("total windows: %d\n", nwindows);
        for (i = 0;i < nwindows;i ++) {
            struct cui_widget_st *widget;
            int id;
            window = cJSON_GetArrayItem(windowlist, i);
            node = cJSON_GetObjectItem(window, CUI_CONFIELD_ENABLE);
            if(cJSON_ValidNumber(node) == 0 || node->valueint != CUI_CONFIG_FIELDVALUE_TRUE){
                continue;
            }

            node = cJSON_GetObjectItem(window, CUI_CONFIELD_ID);
            if (cJSON_ValidNumber(node) == 0) {
                ERRSYS_FATALPRINT("cui config window id invalid, skipped(%p)\n", node);
                continue;
            }

            id = node->valueint;
            node = cJSON_GetObjectItem(window, CUI_CONFIELD_WINDOW);
            if (node != NULL) {
                struct cui_widget_operations *widgetops = get_widget_operation(node->string);
                if (widgetops == NULL) {
                    ERRSYS_INFOPRINT("no operation found for widget %s\n", node->string);
                    continue;
                }
                widget = cui_new_widget(id, cscr, widgetops, NULL, widgetprev);
                // ERRSYS_INFOPRINT("PARSE BEFORE\n");
                // cui_dump_widget_byname(cscr, "dxtviewer");
                retval = widgetops->parse(node, widget);
                ERRSYS_INFOPRINT("\n");
                if(retval == 0){
                    widgetprev = widget;
                    list_add_tail(&widget->node, &cscr->windowlist);
                    ERRSYS_INFOPRINT("widget \"%s\" is added\n", widget->name);
                }
                else {
                    cui_widget_destroy(widget);
                }
                // ERRSYS_INFOPRINT("PARSE AFTER\n");
                // cui_dump_widget_byname(cscr, "dxtviewer");
            }
            
        }
    }

err:
	if(root != NULL){
		cJSON_Delete(root);
	}
	return retval;
}

static struct cui_screen_st* cui_new_screen()
{
    struct cui_screen_st* cscr = zmalloc(sizeof(struct cui_screen_st));
    if (cscr) {
        INIT_LIST_HEAD(&cscr->windowlist);
        INIT_LIST_HEAD(&cscr->layoutgrouplist);
    }

    return cscr;
}

struct cui_screen_st* cui_load(const char* conf)
{
    struct cui_screen_st* cscr = cui_new_screen();
    FILE *fp = NULL;
    char *buffer = NULL;
    int retval = -1;
    if (cscr != NULL) {
	    int filesize = 0;
        fp = fopen(conf, "r");
        if(fp == NULL){
            ERRSYS_FATALPRINT("fopen failed, filepath=%s, err:%s\n", conf, strerror(errno));
            goto err;
        }

        fseek(fp, 0, SEEK_END);
        filesize = ftell(fp);

        if(filesize < 2){
            ERRSYS_FATALPRINT("invalid file(%s) length(%d)\n", conf, filesize);
            goto err;
        }

        buffer = (char *)malloc(filesize *sizeof(char));
        if(buffer == NULL){
            ERRSYS_FATALPRINT("fail to malloc %d for buffer\n", filesize);
            goto err;
        }

        fseek(fp, 0, SEEK_SET);
        if(fread(buffer, filesize, sizeof(char), fp) < 0){
            ERRSYS_FATALPRINT("read %s failed, err:%s\n", conf, strerror(errno));
            goto err;
        }
        ERRSYS_INFOPRINT("\n");
        ERRSYS_INFOPRINT("### parse widget BEGIN ###\n");
        if (cui_initialize(cscr, buffer) != 0) {
            ERRSYS_FATALPRINT("cui initialize failed\n");
            goto err;
        }

        // cui_dump_widget_byname(cscr, "dxtviewer");

        ERRSYS_INFOPRINT("\n");
        ERRSYS_INFOPRINT("### create widget BEGIN ###\n");
        if (cui_ops_widget_recursive_widthfirst(&cscr->windowlist, CUI_WIDGET_OPS_RECURSIVE_TYPE_CREATE) != 0) {
            ERRSYS_FATALPRINT("cui create widget failed\n");
            goto err;
        }
        ERRSYS_INFOPRINT("### create widget END ###\n");

        // call default initializer of the widgets
        ERRSYS_INFOPRINT("\n");
        ERRSYS_INFOPRINT("### initialize(customized) widget BEGIN ###\n");
        if (cui_ops_widget_recursive_widthfirst(&cscr->windowlist, CUI_WIDGET_OPS_RECURSIVE_TYPE_CUSTOMIZED_INITIALIZE) != 0) {
            ERRSYS_FATALPRINT("cui initialize widget failed\n");
            goto err;
        }
        ERRSYS_INFOPRINT("### initialize(customized) widget END ###\n");
        retval = 0;
    }
err:
    if (fp != NULL) {
        fclose(fp);
    }
    if (buffer != NULL) {
        free(buffer);
    }
    if (retval != 0) {
        // release cui
        if (cscr != NULL) {
            cui_unload(cscr, 0);
            cscr = NULL;
        }
    }
    return cscr;
}

void cui_refresh(struct cui_screen_st *cscr, int cursor_visibility)
{
    cscr->refresh = 1;
    if (cursor_visibility >= CUI_CURSOR_INVISIBLE && cursor_visibility <= CUI_CURSOR_VERYVISIBLE) {
        cscr->cursor_visibility = cursor_visibility;
        curs_set(cscr->cursor_visibility);
    }
}

int cui_isrefreshed(struct cui_screen_st *cscr)
{
    return cscr->refresh == 1;
}

int cui_screen_clear(struct cui_screen_st *cscr)
{
    cscr->stack.count = 0;
    return 0;
}

struct cui_widget_st* cui_screen_peep(struct cui_screen_st *cscr)
{
    struct cui_widget_st* widget = NULL;
    if (cscr->stack.count > 0) {
        widget = cscr->stack.widgets[cscr->stack.count - 1];
    }

    return widget;
}

int cui_issubwidget(struct cui_widget_st *child, struct list_head *widgetlist)
{
    // ERRSYS_INFOPRINT("%s child %p\n", __func__, child);
    if (child != NULL && widgetlist != NULL && !list_empty(widgetlist)) {
        struct cui_widget_st *widget;
        // ERRSYS_INFOPRINT("%s: child %s\n", __func__, child->name);
        list_for_each_entry(widget, widgetlist, node) {
            if (widget == child) {
                return 1;
            }
            cui_issubwidget(child, &widget->widgetlist);
        }
    }

    return 0;
}

struct cui_widget_st* cui_screen_pop(struct cui_screen_st *cscr)
{
    struct cui_widget_st* widget = NULL;
    struct cui_widget_st *inputwidget = NULL;
    
    if (cscr->stack.count > 0) {
        widget = cscr->stack.widgets[cscr->stack.count - 1];
        cscr->stack.count --;
        ERRSYS_INFOPRINT("screen pop: top widget %s is popped\n",widget->name);
    }

    if (cscr->stack.count > 0) {
        widget = cscr->stack.widgets[cscr->stack.count - 1];
        if (widget != NULL) {
            ERRSYS_INFOPRINT("screen pop: new top widget %s\n",widget->name);
            inputwidget = cui_get_default_inputwidget(widget);
            if (inputwidget != NULL) {
                ERRSYS_INFOPRINT("screen pop: new input widget %s\n",inputwidget->name);
                cui_update_inputwidget(inputwidget);
            }
            else {
                ERRSYS_INFOPRINT("screen pop: new input widget null\n");
            }
            
        }
    }
    else {
        cscr->inputwidget = NULL;
    }

    return widget;
}

int cui_screen_push(struct cui_widget_st *widget, struct cui_widget_st *inputwidget)
{
    int retval = -1;
    if (widget != NULL) {
        struct cui_screen_st *cscr = widget->cscr;
        if (cscr->stack.count < CUI_SCRSTACK_SIZE) {
            cscr->stack.widgets[widget->cscr->stack.count] = widget;
            cscr->stack.count ++;
            retval = 0;
            // ERRSYS_INFOPRINT("screen push: top widget %s\n",widget->name);
        }
        // cui_dump_screen(cscr);
        /* 如果当前键盘事件窗口不是新widget的子窗口，那么为新widget设置键盘事件窗口 */
        // ERRSYS_INFOPRINT("%s: widget %p\n", __func__, widget);
        // ERRSYS_INFOPRINT("%s: widget->cscr %p\n", __func__, widget->cscr);
        // ERRSYS_INFOPRINT("%s: widget->cscr->inputwidget %p\n", __func__, widget->cscr->inputwidget);
        if (inputwidget == NULL && !cui_issubwidget(widget->cscr->inputwidget, &widget->widgetlist)) {
            // ERRSYS_INFOPRINT("!!!!! %s: looking for new inputwidget for %s\n", __func__, widget->name);
            inputwidget = cui_get_default_inputwidget(widget);
            // if (inputwidget) {
            //     ERRSYS_INFOPRINT("!!!!! %s: new inputwidget %s\n", __func__, inputwidget->name);
            // }
            // else {
            //     ERRSYS_INFOPRINT("!!!!! %s: new inputwidget NULL\n", __func__);
            // }
        }
        // if (widget->cscr->inputwidget) {
        //     ERRSYS_INFOPRINT("%s: cur inputwidget %s\n", __func__, widget->cscr->inputwidget->name);
        // }
        // else {
        //     ERRSYS_INFOPRINT("%s: cur inputwidget NULL\n", __func__);
        // }
        // if (inputwidget) {
        //     ERRSYS_INFOPRINT("%s: new inputwidget %s\n", __func__, inputwidget->name);
        // }
        // else {
        //     ERRSYS_INFOPRINT("%s: new inputwidget NULL\n", __func__);
        // }

        cui_update_inputwidget(inputwidget);
        retval = 0;
    }

    return retval;
}

void cui_update_inputwidget(struct cui_widget_st *widget)
{
    // ERRSYS_INFOPRINT("%s: begin\n", __func__);
    if (widget != NULL) {
        // ERRSYS_INFOPRINT("%s: widget->cscr->inputwidget %p\n", __func__, widget->cscr->inputwidget);
        if (widget->cscr->inputwidget != NULL) {
            keypad(widget->cscr->inputwidget->win, FALSE);
            ERRSYS_INFOPRINT("input widget:%s->%s\n", widget->cscr->inputwidget->name, widget->name);
        }
        else {
            ERRSYS_INFOPRINT("input widget:%s\n", widget->name);   
        }
        widget->cscr->inputwidget = widget;
        keypad(widget->win, TRUE);
    }
    // ERRSYS_INFOPRINT("%s: end\n", __func__);
}



void cui_draw(struct cui_screen_st* cscr)
{
    struct cui_widget_st *widget = cui_screen_peep(cscr);
    ERRSYS_INFOPRINT("\n");
    ERRSYS_INFOPRINT("### draw widget BEGIN ###\n");
    cui_dump_screen(cscr);
    if (widget != NULL && cui_isrefreshed(cscr)) {
        struct cui_widget_st* inputwidget = NULL;
        ERRSYS_INFOPRINT("[%s] draw\n", widget->name);
        ERRSYS_INFOPRINT("[%s] cb.paint %p\n", widget->name, widget->cb.paint);
        if (widget->cb.paint) {
            widget->cb.paint(widget, 0);
        }
        widget->ops.paint(widget);
        cui_ops_widget_recursive_widthfirst(&widget->widgetlist, CUI_WIDGET_OPS_RECURSIVE_TYPE_PAINT);
        if (cscr->inputwidget == NULL) {
            if (inputwidget != NULL) {
                ERRSYS_INFOPRINT("default input widget [%s]\n", inputwidget->name);
                cui_update_inputwidget(inputwidget);
            }
            else {
                ERRSYS_INFOPRINT("input widget not found under [%s]\n", widget->name);
            }
        }
        cscr->refresh = 0;
    }
    ERRSYS_INFOPRINT("### draw widget END ###\n");
}

void cui_eventloop(struct cui_screen_st* cscr)
{
    int handled = CUI_WIDGETEVENT_PROPAGATION;
    int c;

    ERRSYS_INFOPRINT("\n");
    ERRSYS_INFOPRINT("cui eventloop begin\n");
    if (cscr->inputwidget) {
        struct cui_widget_st *widget = cscr->inputwidget;
        if (widget != NULL) {
            c = wgetch(widget->win);
            do {
                /* inputwidget可能更新 */
                ERRSYS_INFOPRINT("%s input widget %s\n", __func__, widget->name);
                handled = widget->ops.keyevent(widget, c);
                if (handled != CUI_WIDGETEVENT_PROPAGATION) {
                    ERRSYS_INFOPRINT("[%s] KEY EVENT is handled, break!\n", widget->name);
                    break;
                }
                widget = widget->parent;
            } while (handled == CUI_WIDGETEVENT_PROPAGATION && widget != NULL);
        }
    }
    ERRSYS_INFOPRINT("cui eventloop end\n");
}

eventcb_function register_widget_callback(struct cui_widget_st *widget, int cbid, eventcb_function cb)
{
    eventcb_function cb_orig = NULL;
    if (widget != NULL) {
        switch (cbid) {
            case CUI_WIDGETEVENT_CALLBACK_KEYEVENT:
            cb_orig = widget->cb.keyevent;
            widget->cb.keyevent = cb;
            break;
        }
    }
    return cb_orig;
}
